iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
佛心分享-SideProject30

30 天開發一款真正能每天使用的散步 App系列 第 7

基礎散步流程(3)——背景定位

  • 分享至 

  • xImage
  •  

雖然昨天已經完成了基本的散步功能,但要讓它真正能在日常使用,還有一個很重要的功能沒有完成,也就是要能夠在背景定位。

因為大部分人都不會把手機一直停留在 App 畫面,可能會鎖屏、切換到其他 App,甚至開走後就不會拿出來看。

背景定位本身並不是什麼困難的技術,真正需要花時間思考的是整條資料流該如何設計。

將定位生命週期與畫面分離

目前定位流程如下:

進入頁面
  ↓
Component 執行 watchPosition
  ↓
呼叫 LocationService 開始定位
  ↓
更新 React State
  ↓
收到 GPS 點位
  ↓
重新渲染地圖路線

前面幾天已經把定位操作整理到 LocationService,定位仍然是由 UI 啟動與停止的。

但實際散步時,使用者不會一直看著 App。他們可能會鎖定螢幕、切換到其他 App,或暫時離開散步畫面。即使畫面不在前景,這次散步仍然還沒有結束,GPS 也應該繼續記錄。

所以流程應該是這樣的:

 LocationManager
        ↓
 啟動平台背景定位
        ↓
 收到 GPS 點位
        ↓
 GPS 點位過濾
        ↓
 SQLite 儲存點位
        ↓
通知 React Component 更新畫面

各自負責的工作如下:

  • LocationManager 負責管理整段散步的定位流程
  • LocationService 負責呼叫底層定位功能並回傳 GPS 點位
  • SQLite 負責保存散步軌跡
  • React State 只保留目前畫面需要顯示的資料
  • Component 被卸載時,不會直接中斷正在進行的散步
class LocationManager {
  async startTracking() {
    // 啟動背景定位
  }

  async pauseTracking() {
    // 暫停記錄點位
  }

  async resumeTracking() {
    // 恢復定位
  }

  async stopTracking() {
    // 停止背景定位
  }

  async handleLocation(location: Location) {
    // 過濾 GPS 點
    // 寫入 SQLite
    // 通知目前畫面更新
  }
}

畫面本身只需要訂閱最新的位置資料:

useEffect(() => {
  const unsubscribe = LocationManager.subscribe(location => {
    setLatestLocation(location);
  });

  return unsubscribe;
}, []);

這樣定位會跟著散步是否正在進行運作,而不是依賴 UI 是否仍然存在。

背景定位的權限

另一個很重要的部分就是權限問題。

背景定位不像相機或一般定位只詢問一次權限即可,Android 和 iOS 都建議依照不同階段逐步請求權限,而不是一開始全部一起跳出,不然有些使用者可能會覺得為什麼要請求那麼多權限?

只有確認背景定位權限已經授權後,才允許使用者開始散步。

不同權限狀態也需要對應不同處理方式。

  • Denied:再次說明用途,允許重新請求。
  • Blocked:直接引導使用者前往系統設定,而不是一直重複跳出權限視窗。

雖然只是一些流程控制,但這些細節會直接影響使用者的體驗。如果沒有規劃好,很容易讓使用者以為 App 無法正常運作。

Android 實現背景定位

Android 對背景執行的限制比 iOS 更嚴格一點,即使已經取得背景定位權限,也不代表 App 可以持續在背景接收 GPS 更新。

如果要在Android背景執行定位,就必須透過 Foreground Service,並在通知列顯示一則持續通知。

像下面這樣:

AndroidManifest 設定

除了定位權限之外,也需要宣告 Foreground Service 相關權限。

android/app/src/main/AndroidManifest.xml 上方需要加上:

// 基本定位需要的權限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
// 背景定位需要的權限
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />

Android 對 Foreground Service 的限制越來越嚴格,因此建議一開始就完整設定,避免後續升級 Android SDK 時才發現服務無法正常啟動。

Foreground Service 的生命週期

Android 的背景定位需要透過 Foreground Service 持續執行,因此我另外建立了一個 TraceWalkLocationService,專門負責讓定位工作能在 Android 背景繼續運作。

開始散步時,App 會:

  1. 啟動 TraceWalkLocationService
  2. 顯示定位通知
  3. 開始定位
  4. 約每 x 秒取得一次位置
  5. 移動超過約 x 公尺才記錄一個 GPS 點位

之所以要限制第四點和第五點是因為更新太頻繁會耗電,如果一直在原地打轉的話路線就會非常詭異,所以一定要做限制。

暫停散步時,不用停止 TraceWalkLocationService,而是只停止 GPS 更新,並修改通知顯示的內容。

原因是 Foreground Service 一旦停止,Android 對背景程式的保護也會一起消失。而我的APP有「暫停」的功能,如果使用者去上廁所、買個飲料的話就可以暫停不繼續定位,等到使用者按下「繼續」時,只要重新開始定位,不需要重新建立整個背景服務。

直到真正結束散步,才會停止定位、關閉 TraceWalkLocationService,並移除通知。

iOS 實現背景定位

和 Android 相比,iOS 不需要 Foreground Service,也不會要求 App 在通知列持續顯示一則通知。

但如果沒有按照官方建議的方式設定,定位很容易在鎖屏或背景後停止更新。

Info.plist 設定

除了在 Xcode 的 Signing & Capabilities 中開啟 Background Modes → Location updates,還需要在 Info.plist 加入定位權限說明。前者是告訴 iOS 允許 App 在背景持續取得位置,後者則是在系統詢問定位權限時,向使用者說明 App 為什麼需要存取位置資訊。

  1. Targets > Signing & Capabilities > Background Modes > 打勾 Location Updates
  2. 在 /ios/TraceWalk/Info.plist 新增 NSLocationAlwaysAndWhenInUseUsageDescription
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>TraceWalk 需要在散步期間持續取得位置,即使螢幕鎖定或 App 位於背景。</string>
    

這樣不論是使用者或審核人員,都能理解背景定位存在的必要性。

React Native 的設定

定位功能我使用的是 @react-native-community/geolocation 套件,在初始化時需要先設定背景定位相關選項:

import Geolocation, {
    type GeolocationError,
    type GeolocationResponse,
} from '@react-native-community/geolocation';

Geolocation.setRNConfiguration({
    authorizationLevel: 'always',
    enableBackgroundLocationUpdates: true, // 允許 App 在背景持續接收位置更新
});

這兩個屬性都得填寫正確,如果缺少其中一項,都可能導致鎖屏後定位停止更新。


上一篇
基礎散步流程(2)——GPS 定位點過濾
下一篇
基礎散步流程(4)——完成散步統計
系列文
30 天開發一款真正能每天使用的散步 App12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言